#include "ogrebindingPCH.h"
#include "quaternionbinding.h"
#include "vector3binding.h"

namespace lua_ogre {

const char* QuaternionClassName = "Quaternion";

static int GetLeft(Ogre::Quaternion *self, lua_State *ls)
{
	return LuaCreateVector3(self->xAxis(), ls);
}

static int GetUp(Ogre::Quaternion *self, lua_State *ls)
{
	return LuaCreateVector3(self->yAxis(), ls);
}

static int GetForward(Ogre::Quaternion *self, lua_State *ls)
{
	return LuaCreateVector3(self->zAxis(), ls);
}

static LuaMethod<Ogre::Quaternion> g_quat_methods[] = 
{
	{"left", 0, GetLeft, 0},
	{"up", 0, GetUp, 0},
	{"forward", 0, GetForward, 0},
	{0, 0}
};

int LuaCreateQuaternion(const Ogre::Quaternion &quat, lua_State *ls)
{
	Ogre::Quaternion *quat_ptr = (Ogre::Quaternion*)lua_newuserdata(ls, sizeof(Ogre::Quaternion));
	*quat_ptr = quat;
	luaL_getmetatable(ls, QuaternionClassName);
	lua_setmetatable(ls, -2);
	return 1;
}

static int QuatAlloc(lua_State *ls)
{
	lua_remove(ls, 1);

	const char *create_failed = "create Quaternion failed! invalid parameter!";
	Ogre::Quaternion quat;
	int arg_count = lua_gettop(ls);
	if (arg_count == 2)
	{
		// vector3 + angle
		if (luaL_checkudata(ls, 1, Vector3ClassName)
			&& lua_isnumber(ls, 2))
		{
			const Ogre::Vector3 &vec = *(Ogre::Vector3*)lua_touserdata(ls, 1);
			Ogre::Degree angle((float)lua_tonumber(ls, 2));
			quat.FromAngleAxis(Ogre::Radian(angle), vec);
		}
		else
		{
			LogLuaError << create_failed;
			return 0;
		}
	}
	else if (arg_count == 1)
	{
		if (luaL_checkudata(ls, 1, QuaternionClassName))
		{
			quat = *(Ogre::Quaternion*)lua_touserdata(ls, 1);
		}
		else
		{
			LogLuaError << create_failed;
			return 0;
		}
	}
	else
	{
		LogLuaError << create_failed;
		return 0;
	}

	Ogre::Quaternion *quat_ptr = (Ogre::Quaternion*)lua_newuserdata(ls, sizeof(Ogre::Quaternion));
	if (NULL == quat_ptr)
	{
		LogLuaError << create_failed;
	}
	*quat_ptr = quat;
	luaL_getmetatable(ls, QuaternionClassName);
	lua_setmetatable(ls, -2);
	return 1;
}

static int QuatToString(lua_State *ls)
{
	const Ogre::Quaternion &quat = *(Ogre::Quaternion*)lua_touserdata(ls, 1);
	lua_pushfstring(ls, "Quaternion(%f, %f, %f, %f)",
		quat.w, quat.x, quat.y, quat.z);
	return 1;
}

bool BindQuaternion(lua_State *ls)
{
	luaL_newmetatable(ls, QuaternionClassName);
	int metatable = lua_gettop(ls);

	lua_pushvalue(ls, metatable);
	lua_setmetatable(ls, metatable);

	luaL_Reg empty_reg = {NULL, NULL};
	luaL_register(ls, "ov", &empty_reg);
	int module_table = lua_gettop(ls);
	lua_pushstring(ls, QuaternionClassName);
	lua_pushvalue(ls, metatable);
	lua_rawset(ls, module_table);
	lua_pop(ls, 1);

	lua_pushstring(ls, "__tostring");
	lua_pushcfunction(ls, QuatToString);
	lua_rawset(ls, metatable);

	// it doesn't need __gc
	
	lua_pushstring(ls, "__call");
	lua_pushcfunction(ls, QuatAlloc);
	lua_rawset(ls, metatable);
	
	lua_pushstring(ls, "__index");
	lua_pushcfunction(ls, LuaClassIndex<Ogre::Quaternion>);
	lua_rawset(ls, metatable);

	lua_pushstring(ls, "__newindex");
	lua_pushcfunction(ls, LuaClassSetter<Ogre::Quaternion>);
	lua_rawset(ls, metatable);

	LuaMethod<Ogre::Quaternion> *m = g_quat_methods;
	while (m->name != NULL)
	{
		lua_pushstring(ls, m->name);
		lua_pushlightuserdata(ls, m);
		lua_rawset(ls, metatable);
		++m;
	}

	lua_pop(ls, 1);
	return true;
}

}